package pacBioSimple;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
//import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class PacBioMerger {

	String fileNamelinkerHit=null;
	String outputFile = null;
	PrintWriter results =null;
	public PacBioMerger(String filename,String outputFile) {
		this.fileNamelinkerHit = filename;
		this.outputFile = outputFile;
	}
	
	
	class StreamGobbler implements Callable<Map<Integer, List<Integer> >>
	{
		InputStream is;
		boolean type;
		public StreamGobbler(InputStream is) {
			
			this.is =is;
			
			
			// TODO Auto-generated constructor stub
		}
		@Override
		public Map<Integer, List<Integer> > call() throws Exception {
			// TODO Auto-generated method stub
			
			
			String delim = "[ \t]";
			boolean scores = false;
			Map<Integer, List<Integer> > alignments = new HashMap<>();
			List<Integer> validSeq = new ArrayList<>();
			int current_seq =0;
			BufferedReader brf = new BufferedReader(new InputStreamReader(is));

			String line ="";
			
			while((line=brf.readLine())!=null)
			{
				//System.out.println(line);
				if(line.length()>0)
				{
					String[] token = line.split(delim);
					if(token.length>0 && !scores && token[0].equals("Sequences"))
					{
						scores = true;
					}
					else if(token.length>0 && scores && token[0].equals("Guide"))
					{
						scores = false;
						break;
					}
					
					if(token.length>0 && scores)
					{
						String[] sequences = token[1].split("[:]");
						
						if(current_seq < Integer.valueOf(sequences[0].substring(1)) || current_seq==0)
						{
							validSeq = new ArrayList<>();
							current_seq = Integer.valueOf(sequences[0].substring(1));
							alignments.put(current_seq, validSeq);
						}
						if(Integer.valueOf(token[5])>50)
						{
							//check if we already have the entry in the list
							alignments.get(current_seq).add(Integer.valueOf(sequences[1].substring(0,sequences[1].length()-1)));
						}
					}
				}
			}
			brf.close();
			return alignments;				
		}
		
	}

	
	
	
	private void doClustalw2(PacBioLinkerObject pbl) throws Exception
	{
		//first write the fasta file
		
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(new File(outputFile+"_"+".fasta"))));
		
			for(int i=0; i < pbl.getFastaSeq().size();++i)
			{
				pw.println(">"+i);
				pw.println(pbl.getFastaSeq().get(i));
			}
			pw.flush();
			pw.close();
			//now we have call clustalw2
			
			StringBuffer command =new StringBuffer("perl /home/patel/git/filter/pacBio/sourc_code/linker/callClustalw.pl -i "+outputFile+"_"+".fasta"+" -o "+outputFile+"_clustalw_result");
			
			Process p = MyUtil.callProcessAndGetProcess(command.toString());
			
			ExecutorService executor = Executors.newCachedThreadPool();
			Future<Map<Integer, List<Integer>>> error = executor.submit(new StreamGobbler(p.getInputStream()));
			
			int exit_status = p.waitFor();
//			BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
			
			Map<Integer, List<Integer> > alignments = error.get();
//			String s ="";
//			if((s=stdError.readLine())!=null)
//			{
//				System.out.println("clustalw problem");
//				System.out.println(s);
//			}

		command =new StringBuffer("perl /home/patel/git/filter/pacBio/sourc_code/linker/callRM.pl -i "+outputFile+"_*");
									
		exit_status = MyUtil.callProcess(command.toString());
	
		if(exit_status>0)
		{
			System.err.println("remove failed!");
		}
		Map<Integer,Map<Integer,Boolean>> summary = new HashMap<>();	
		Iterator<Entry<Integer, List<Integer>>> it =alignments.entrySet().iterator();
		Map<Integer,Boolean> removed = new HashMap<>();
		while(it.hasNext())
		{
			Entry<Integer,List<Integer>> index = it.next();
			for(int i=0; i < index.getValue().size();++i)
			{
				addElements(alignments, summary,index.getKey() ,index.getValue().get(i),removed);
			}
		}
		
		//now we have to build up the sequences for mapfft
		try
		{
			callMafft(summary,pbl);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	private void callMafft(Map<Integer,Map<Integer,Boolean>> summary,PacBioLinkerObject pbl) throws Exception
	{
		boolean temp=false;
		int counter=0;
		List<Callable<String>> toCall = new ArrayList<>();
		for(Entry<Integer, Map<Integer,Boolean> > entry : summary.entrySet() )
		{
			
			StringBuffer sb = new StringBuffer();
			
			sb.append(">"+(entry.getKey()-1));
			sb.append("\n");
			sb.append(pbl.getFastaSeq().get((entry.getKey()-1)));
			sb.append("\n");
			for(Entry<Integer, Boolean> innerEntry : entry.getValue().entrySet())
			{
				sb.append(">"+(innerEntry.getKey()-1));
				sb.append("\n");
				sb.append(pbl.getFastaSeq().get((innerEntry.getKey()-1)));
				sb.append("\n");
			}
			
			if(temp)
			{
				toCall.add(new PacBioMAFFTCall(counter, this.outputFile, sb,pbl.getFastaName()));
			}
			else
			{
			
				PacBioMAFFTCall mafftCall = new PacBioMAFFTCall(counter, this.outputFile, sb,pbl.getFastaName());
				String result = mafftCall.call();
				
				if(results == null)
				{
					results = new PrintWriter(new BufferedWriter(new FileWriter(new File(outputFile))));
				}
				results.println(pbl.fastaName);
				results.println(result);
				//results.flush();
			}
			
			
			++counter;
		}
	}
	
	private void addElements(Map<Integer,List<Integer>>goodAlignments,Map<Integer,Map<Integer,Boolean>> summary, int first_key,int second_key,Map<Integer,Boolean> removed)
	{
		//first add value
		if(!removed.containsKey(first_key))
		{
			if(!summary.containsKey(first_key))
			{
				Map<Integer, Boolean> toBealign = new HashMap<>();
				summary.put(first_key, toBealign);
			}
			if(!summary.get(first_key).containsKey(second_key))
			{
				summary.get(first_key).put(second_key, Boolean.TRUE);
			}		
			//now we have to add the links
			if(goodAlignments.containsKey(second_key))
			{
				List<Integer> goodAlign = goodAlignments.get(second_key);
				List<Integer> checkAlignment = new ArrayList<>();
				for(int i=0; i < goodAlign.size();++i)
				{
					if(!summary.containsKey(first_key))
					{
						Map<Integer, Boolean> toBealign = new HashMap<>();
						summary.put(first_key, toBealign);
					}
					if(!summary.get(first_key).containsKey(goodAlign.get(i)))
					{
						summary.get(first_key).put(goodAlign.get(i), Boolean.TRUE);
						checkAlignment.add(goodAlign.get(i));
					}
				}
				
				for(int i=0; i < checkAlignment.size();++i)
				{
					addElements(goodAlignments, summary, first_key, checkAlignment.get(i),removed);
				}
			}	
			//goodAlignments.remove(second_key);
			removed.put(second_key, Boolean.TRUE);
		}
		
	}
	
	
	
	public void read()
	{
		PacBioLinkerObject toBeAlign = new PacBioLinkerObject();
		try(BufferedReader bfr = new BufferedReader(new FileReader(new File(fileNamelinkerHit))))
		{
			String prev ="";
			
			while(bfr.ready())
			{
				if(prev.equals(""))
				{
					prev = bfr.readLine();
				}
				if(prev.charAt(0)=='>')
				{
					//now read until 
					toBeAlign = new PacBioLinkerObject(prev);
					
					List<String> seq = new ArrayList<>();
					while(bfr.ready())
					{
						String sequence = bfr.readLine();
						if(sequence.charAt(0)=='>')
						{
							prev=sequence;
							break;
						}
						seq.add(sequence);
					}
					toBeAlign.setFastaSeq(seq);
					System.out.println(toBeAlign.fastaName);
					doClustalw2(toBeAlign);
				}
				
			}
			results.flush();
			results.close();
			
		}
		catch (Exception e) {
			e.printStackTrace();
			System.out.println(toBeAlign.fastaName);
			System.exit(1);
		}
	}
	
	
}
